#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Datetime: 2021/12/05 13:14
# @Author  : CHEN Wang
# @Site    :
# @File    : bitmex.py
# @Software: PyCharm

"""
脚本说明: 从bitmex api上获取相关信息

bitmex api
具体api文档参考:https://www.bitmex.com/api/explorer/#/
"""


import requests
import json
import datetime
import time
from dateutil.tz import tzutc
import pandas as pd
import numpy as np
from requests.adapters import HTTPAdapter
from urllib3.util import Retry
from requests.exceptions import ConnectTimeout, SSLError, ReadTimeout, ConnectionError
from retrying import retry
from urllib.parse import urljoin
from quant_researcher.quant.project_tool.time_tool import datetime_to_timestamp, timestamp_to_str, calc_date_diff, guess_date_fmt, date_shifter, str_to_timestamp
from quant_researcher.quant.project_tool.logger.my_logger import LOG

TIMEOUT = 10
ILOVECHINA = "同学！！你知道什么叫做科学上网么？ 如果你不知道的话，蓝灯，喵帕斯，VPS，阴阳师，v2ray，随便来一个！"
Bitmex_base_url = "https://www.bitmex.com/api/v1/"

MAX_HISTORY = 750
"""
QUANTAXIS 和 Bitmex 的 frequency 常量映射关系
"""
Bitmex2QA_FREQUENCY_DICT = {
    "1m": '1min',
    "5m": '5min',
    "15m": '15min',
    "30m": '30min',
    "60m": '60min',
    "1h": '60min',
    "1d": 'day',
}

FREQUENCY_SHIFTING = {
    "1m": 43200,
    "5m": 225000,
    "15m": 675000,
    "30m": 1350000,
    "1h": 2700000,
    "1d": 64800000
}


common_crypto = {'BTC-USD': 'XBTUSD',
                 'BTC-USDT': 'XBTUSDT'}


def format_btimex_data_fields(datas, frequency):
    # 归一化数据字段，转换填充必须字段，删除多余字段
    frame = pd.DataFrame(datas)
    frame['symbol'] = frame['symbol'].apply(lambda x: 'BITMEX.{}'.format(x))
    # UTC时间转换为北京时间，接收到的数据有时候 tz-aware 有时候又是变成 non tz-aware，
    # 改了几次代码，既不能单纯 tz_localize 也不能单纯 tz_convert
    # dt.tz_localize(None) 是 Stackoverflow 的解决方案，先观察效果
    frame['datetime'] = pd.to_datetime(
        frame['timestamp']
    ).dt.tz_localize(None).dt.tz_localize('Asia/Shanghai')
    frame['date'] = frame['datetime'].dt.strftime('%Y-%m-%d')
    frame['datetime'] = frame['datetime'].dt.strftime('%Y-%m-%d %H:%M:%S')
    # GMT+0 String 转换为 UTC Timestamp
    frame['time_stamp'] = pd.to_datetime(frame['timestamp']
                                         ).astype(np.int64) // 10 ** 9
    frame['date_stamp'] = pd.to_datetime(frame['date']
                                         ).astype(np.int64) // 10 ** 9
    frame['created_at'] = int(
        time.mktime(datetime.datetime.now().utctimetuple())
    )
    frame['updated_at'] = int(
        time.mktime(datetime.datetime.now().utctimetuple())
    )
    frame.rename({'trades': 'trade'}, axis=1, inplace=True)
    frame['amount'] = frame['volume'] * (frame['open'] + frame['close']) / 2
    frame.drop(
        [
            'foreignNotional',
            'homeNotional',
            'lastSize',
            'timestamp',
            'turnover',
            'vwap'
        ],
        axis=1,
        inplace=True
    )
    if (frequency not in ['1day', Bitmex2QA_FREQUENCY_DICT['1d'], '1d']):
        frame['type'] = Bitmex2QA_FREQUENCY_DICT[frequency]
    return frame


@retry(stop_max_attempt_number=3, wait_random_min=50, wait_random_max=100)
def QA_fetch_bitmex_symbols(active=True):
    """
    默认为获取活跃交易对，不获取下架交易对数据
    """
    if active:
        url = urljoin(Bitmex_base_url, "instrument/active")
    else:
        url = urljoin(Bitmex_base_url, "instrument")

    retries = 1
    datas = list()
    while (retries != 0):
        try:
            req = requests.get(url, params={"count": 500}, timeout=TIMEOUT)
            retries = 0
        except (ConnectTimeout, ConnectionError, SSLError, ReadTimeout):
            retries = retries + 1
            if (retries % 6 == 0):
                print(ILOVECHINA)
            print("Retry instrument/active #{}".format(retries - 1))
            time.sleep(0.5)

        if (retries == 0):
            # 成功获取才处理数据，否则继续尝试连接
            symbol_lists = json.loads(req.content)
            if len(symbol_lists) == 0:
                return []
            for symbol in symbol_lists:
                datas.append(symbol)

    return datas


@retry(stop_max_attempt_number=3, wait_random_min=50, wait_random_max=100)
def QA_fetch_bitmex_kline_with_auto_retry(
        symbol,
        start_time,
        end_time,
        frequency,
):
    """
    Get the latest symbol‘s candlestick data raw method
    """
    url = urljoin(Bitmex_base_url, "trade/bucketed")
    retries = 1
    while (retries != 0):
        try:
            start_epoch = datetime.datetime.fromtimestamp(
                start_time,
                tz=tzutc()
            )
            end_epoch = datetime.datetime.fromtimestamp(end_time, tz=tzutc())
            req = requests.get(
                url,
                params={
                    "symbol": symbol,
                    "binSize": frequency,
                    "startTime": start_epoch.isoformat(),
                    "endTime": end_epoch.isoformat(),
                    "count": MAX_HISTORY
                },
                timeout=TIMEOUT
            )
            # 防止频率过快被断连
            time.sleep(0.5)
            retries = 0
        except (ConnectTimeout, ConnectionError, SSLError, ReadTimeout):
            retries = retries + 1
            if (retries % 6 == 0):
                print(ILOVECHINA)
            print("Retry trade/bucketed #{}".format(retries - 1))
            time.sleep(0.5)

        if (retries == 0):
            # 防止频率过快被断连
            remaining = int(req.headers['x-ratelimit-remaining'])
            if remaining < 20:
                time.sleep(0.5)
            elif remaining < 10:
                time.sleep(5)
            elif remaining < 3:
                time.sleep(30)

            # 成功获取才处理数据，否则继续尝试连接
            klines = json.loads(req.content)
            return klines

    return None


def QA_fetch_bitmex_kline(
        symbol,
        start_time,
        end_time,
        frequency,
        callback_func=None
):
    """
    Get the latest symbol‘s candlestick data
    时间倒序切片获取算法，是各大交易所获取1min数据的神器，因为大部分交易所直接请求跨月跨年的1min分钟数据
    会直接返回空值，只有将 start_epoch，end_epoch 切片细分到 200/300 bar 以内，才能正确返回 kline，
    火币和Bitmex，OKEx 均为如此，直接用跨年时间去直接请求上万bar 的 kline 数据永远只返回最近200条数据。
    """
    datas = list()
    reqParams = {}
    reqParams['from'] = end_time - FREQUENCY_SHIFTING[frequency]
    reqParams['to'] = end_time

    while (reqParams['to'] > start_time):
        if ((reqParams['from'] > datetime_to_timestamp())) or \
                ((reqParams['from'] > reqParams['to'])):
            # 出现“未来”时间，一般是默认时区设置，或者时间窗口滚动前移错误造成的
            LOG.info(
                'A unexpected \'Future\' timestamp got, Please check self.missing_data_list_func param \'tzlocalize\' set. More info: {:s}@{:s} at {:s} but current time is {}'
                    .format(
                    symbol,
                    frequency,
                    timestamp_to_str(reqParams['from']),
                    timestamp_to_str(
                        datetime_to_timestamp()
                    )
                )
            )
            # 跳到下一个时间段
            reqParams['to'] = int(reqParams['from'] - 1)
            reqParams['from'] = int(reqParams['from'] - FREQUENCY_SHIFTING[frequency])
            continue

        klines = QA_fetch_bitmex_kline_with_auto_retry(
            symbol,
            reqParams['from'],
            reqParams['to'],
            frequency,
        )
        if (len(klines) == 0) or \
                ('error' in klines):
            # 出错放弃
            break

        reqParams['to'] = int(reqParams['from'] - 1)
        reqParams['from'] = int(reqParams['from'] - FREQUENCY_SHIFTING[frequency])

        if (klines is None) or \
                (len(klines) == 0) or \
                ((len(datas) > 0) and (len(klines) > 0) and (klines[-1]['timestamp'] == datas[-1]['timestamp'])):
            # 没有更多数据
            break

        datas.extend(klines)

        if (callback_func is not None):
            frame = format_btimex_data_fields(klines, frequency)
            callback_func(frame, Bitmex2QA_FREQUENCY_DICT[frequency])

    if len(datas) == 0:
        return None

    # 归一化数据字段，转换填充必须字段，删除多余字段
    frame = format_btimex_data_fields(datas, frequency)
    return frame


def QA_fetch_bitmex_kline_min(
        symbol,
        start_time,
        end_time,
        frequency,
        callback_func=None
):
    """
    Get the latest symbol‘s candlestick data with time slices
    时间倒序切片获取算法，是各大交易所获取1min数据的神器，因为大部分交易所直接请求跨月跨年的1min分钟数据
    会直接返回空值，只有将 start_epoch，end_epoch 切片细分到 1000 bar 以内，才能正确返回 kline，
    火币和Bitmex，OKEx 均为如此，用上面那个函数的方式去直接请求上万bar 的分钟 kline 数据是不会有结果的。
    """
    reqParams = {}
    reqParams['from'] = end_time - FREQUENCY_SHIFTING[frequency]
    reqParams['to'] = end_time

    requested_counter = 1
    retries = 1
    datas = list()
    while (reqParams['to'] > start_time):
        if ((reqParams['from'] > datetime_to_timestamp())) or \
                ((reqParams['from'] > reqParams['to'])):
            # 出现“未来”时间，一般是默认时区设置，或者时间窗口滚动前移错误造成的
            LOG.info(
                'A unexpected \'Future\' timestamp got, Please check self.missing_data_list_func param \'tzlocalize\' set. More info: {:s}@{:s} at {:s} but current time is {}'
                    .format(
                    symbol,
                    frequency,
                    timestamp_to_str(reqParams['from']),
                    timestamp_to_str(
                        datetime_to_timestamp()
                    )
                )
            )
            # 跳到下一个时间段
            reqParams['to'] = int(reqParams['from'] - 1)
            reqParams['from'] = int(reqParams['from'] - FREQUENCY_SHIFTING[frequency])
            continue

        klines = QA_fetch_bitmex_kline_with_auto_retry(
            symbol,
            reqParams['from'],
            reqParams['to'],
            frequency,
        )
        if (len(klines) == 0) or \
                ('error' in klines):
            # 出错放弃
            break

        reqParams['to'] = int(reqParams['from'] - 1)
        reqParams['from'] = int(reqParams['from'] - FREQUENCY_SHIFTING[frequency])

        if (callback_func is not None):
            frame = format_btimex_data_fields(klines, frequency)
            callback_func(frame, Bitmex2QA_FREQUENCY_DICT[frequency])

        if (len(klines) == 0):
            return None


def func_args_preprocessing(func):
    """Return function that converts list input arguments to comma-separated strings"""

    def input_args(*args, **kwargs):
        # check in **kwargs for lists and booleans
        for v in kwargs:
            kwargs[v] = arg_preprocessing(kwargs[v])
        # check in *args for lists and booleans
        args = [arg_preprocessing(v) for v in args]

        return func(*args, **kwargs)

    return input_args


def arg_preprocessing(arg_v):
    """Return the values of an argument after preprocessing"""

    # check if arg is list and convert it to comma-separated string
    if isinstance(arg_v, list):
        arg_v = ','.join(arg_v)
    # check if arg is boolean and convert it to string
    elif isinstance(arg_v, bool):
        arg_v = str(arg_v).lower()

    return arg_v


def get_comma_separated_values(values):
    """Return the values as a comma-separated string"""

    # Make sure values is a list or tuple
    if not isinstance(values, list) and not isinstance(values, tuple):
        values = [values]

    return ','.join(values)


class BitMEXAPI:
    __API_URL_BASE = Bitmex_base_url

    def __init__(self, api_base_url=__API_URL_BASE):
        self.api_base_url = api_base_url
        self.request_timeout = 120
        self.session = requests.Session()
        retries = Retry(total=5, backoff_factor=0.5, status_forcelist=[502, 503, 504])
        self.session.mount('http://', HTTPAdapter(max_retries=retries))

    def __request(self, url):
        # print(url)
        try:
            response = self.session.get(url, timeout=self.request_timeout)
        except requests.exceptions.RequestException:
            raise

        try:
            response.raise_for_status()
            content = json.loads(response.content.decode('utf-8'))
            return content
        except Exception as e:
            # check if json (with error message) is returned
            try:
                content = json.loads(response.content.decode('utf-8'))
                raise ValueError(content)
            # if no json
            except json.decoder.JSONDecodeError:
                pass

            raise

    def __api_url_params(self, api_url, params, api_url_has_params=False):
        if params:
            # if api_url contains already params and there is already a '?' avoid
            # adding second '?' (api_url += '&' if '?' in api_url else '?'); causes
            # issues with request parametes (usually for endpoints with required
            # arguments passed as parameters)
            api_url += '&' if api_url_has_params else '?'
            for key, value in params.items():
                if type(value) == bool:
                    value = str(value).lower()

                api_url += "{0}={1}&".format(key, value)
            api_url = api_url[:-1]
        return api_url

    # ---------- PING ----------#
    def get_funding_rate(self, symbol='btc', all_data=False, start_date='2021-12-26', end_date='2021-12-28', **kwargs):
        """
        获取资金费率历史数据

        Swap Funding History
        :param str symbol: Instrument symbol. Send a bare series (e.g. XBT) to get data for the nearest expiring contract in that series.
                        You can also send a timeframe, e.g. XBT:quarterly. Timeframes are nearest, daily, weekly, monthly, quarterly, biquarterly, and perpetual.
                        Symbols are case-insensitive.
                        可参考 /instrument/active
        :param start_date: Starting date filter for results. 如'2021-12-26'
        :param end_date: Ending date filter for results. 如'2021-12-28'
        :param kwargs:
            - filter: Generic table filter. Send JSON key/value pairs, such as {"key": "value"}.
                      You can key on individual fields, and do more advanced querying on timestamps. See the Timestamp Docs for more details.
            - columns: Array of column names to fetch. If omitted, will return all columns.
                       Note that this method will always return item keys, even when not specified, so you may receive more columns that you expect.
            - int count: Number of results to fetch. Must be a positive integer.
            - int start: Starting point for results.
            - bool reverse: If true, will sort results newest first. 默认为False
            -
        :return:
        """

        symbol_1 = common_crypto[symbol]
        kwargs['symbol'] = symbol_1
        api_url = '{0}funding'.format(self.api_base_url)
        if all_data:
            res_list = []
            start_num = 0
            Flag = True
            while Flag and (start_num < 100000):
                kwargs['start'] = start_num
                kwargs['count'] = 500
                api_url_kwargs = self.__api_url_params(api_url, kwargs)
                res = self.__request(api_url_kwargs)
                res = pd.DataFrame(res)
                if res.empty:
                    Flag = False  # 数据获取完毕
                else:
                    res_list.append(res)
                    start_num = start_num + 500
                time.sleep(5)
            all_df = pd.concat(res_list, ignore_index=True)

        else:
            date_fmt = guess_date_fmt(start_date)
            day_gap = calc_date_diff(start_date, end_date, step='days', fmt_str=date_fmt)
            if day_gap > 30:
                kwargs['startTime'] = date_shifter(before=end_date, step='days', how_many=-30)
                if kwargs['startTime'] < start_date:
                    kwargs['startTime'] = start_date
                kwargs['endTime'] = end_date

                res_list = []
                Flag = True
                while(kwargs['endTime'] > start_date) and Flag:
                    api_url_kwargs = self.__api_url_params(api_url, kwargs)
                    res = pd.DataFrame(self.__request(api_url_kwargs))
                    if res.empty:
                        Flag = False  # 数据获取完毕
                        continue
                    else:
                        res_list.append(res)
                        kwargs['endTime'] = kwargs['startTime']
                        kwargs['startTime'] = date_shifter(before=kwargs['startTime'], step='days', how_many=-30)
                        if kwargs['startTime'] < start_date:
                            kwargs['startTime'] = start_date
                all_df = pd.concat(res_list, ignore_index=True)
                all_df.drop_duplicates(subset=['timestamp'], inplace=True)
                all_df.sort_values(by='timestamp', inplace=True)

            else:
                kwargs['startTime'] = start_date
                kwargs['endTime'] = end_date
                api_url_kwargs = self.__api_url_params(api_url, kwargs)
                all_df = pd.DataFrame(self.__request(api_url_kwargs))

        all_df['timestamp'] = pd.to_datetime(all_df['timestamp'])
        all_df['timestamp'] = all_df['timestamp'].apply(datetime_to_timestamp) * 1000
        all_df.rename({'fundingRate': 'funding_rate'}, axis=1, inplace=True)
        all_df.sort_values(by='timestamp', inplace=True)

        return all_df

    # # ---------- SIMPLE ----------#
    # @func_args_preprocessing
    # def get_price(self, ids, vs_currencies, **kwargs):
    #     """
    #     获取数字货币的当前最新价，支持不同货币计价； 注意只是最新时刻的数据
    #     Get the current price of any cryptocurrencies in any other supported currencies that you need
    #     :param str ids: id of coins, comma-separated if querying more than 1 coin *refers to coins/list
    #     :param str vs_currencies: vs_currency of coins, comma-separated if querying more than 1 vs_currency *refers to simple/supported_vs_currencies
    #     :param kwargs:
    #         - include_market_cap: true/false to include market_cap, default: false
    #         - include_24hr_vol: true/false to include 24hr_vol, default: false
    #         - include_24hr_change: true/false to include 24hr_change, default: false
    #         - include_last_updated_at: true/false to include last_updated_at of price, default: false
    #     :return:
    #     """
    #
    #     ids = ids.replace(' ', '')
    #     kwargs['ids'] = ids
    #     vs_currencies = vs_currencies.replace(' ', '')
    #     kwargs['vs_currencies'] = vs_currencies
    #
    #     api_url = '{0}simple/price'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # @func_args_preprocessing
    # def get_token_price(self, id, contract_addresses, vs_currencies, **kwargs):
    #     """
    #     获取某个链上某个tokens的最新价格
    #     Get current price of tokens (using contract addresses) for a given platform in any other currency that you need.
    #     :param id: The id of the platform issuing tokens (See asset_platforms endpoint for list of options)
    #     :param contract_addresses: The contract address of tokens, comma separated
    #     :param vs_currencies: vs_currency of coins, comma-separated if querying more than 1 vs_currency *refers to simple/supported_vs_currencies
    #     :param kwargs:
    #         - include_market_cap: true/false to include market_cap, default: false
    #         - include_24hr_vol: true/false to include 24hr_vol, default: false
    #         - include_24hr_change: true/false to include 24hr_change, default: false
    #         - include_last_updated_at: true/false to include last_updated_at of price, default: false
    #     :return:
    #     """
    #
    #     contract_addresses = contract_addresses.replace(' ', '')
    #     kwargs['contract_addresses'] = contract_addresses
    #     vs_currencies = vs_currencies.replace(' ', '')
    #     kwargs['vs_currencies'] = vs_currencies
    #
    #     api_url = '{0}simple/token_price/{1}'.format(self.api_base_url, id)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #     return self.__request(api_url)
    #
    # @func_args_preprocessing
    # def get_supported_vs_currencies(self, **kwargs):
    #     """
    #     所有的计价单位 （即包含常用法币，也包含一些较常用代币）
    #     ['btc', 'eth','ltc','bch','bnb','eos','xrp','xlm','link','dot','yfi',
    #     'usd','aed','ars','aud','bdt','bhd','bmd','brl','cad','chf','clp','cny','czk','dkk','eur','gbp','hkd','huf',
    #     'idr','ils','inr','jpy','krw','kwd','lkr','mmk','mxn','myr','ngn','nok','nzd','php', 'pkr','pln','rub','sar',
    #     'sek','sgd','thb','try','twd','uah','vef','vnd','zar','xdr','xag','xau','bits','sats']
    #     Get list of supported_vs_currencies
    #     """
    #
    #     api_url = '{0}simple/supported_vs_currencies'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # # ---------- COINS ----------#
    # @func_args_preprocessing
    # def get_coins(self, **kwargs):
    #     """List all coins with data (name, price, market, developer, community, etc)"""
    #
    #     api_url = '{0}coins'.format(self.api_base_url)
    #     # ['order', 'per_page', 'page', 'localization']
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # @func_args_preprocessing
    # def get_coins_list(self, **kwargs):
    #     """
    #     列出所有支持的代币的id，名称，代码
    #     List all supported coins id, name and symbol (no pagination required)
    #     :param kwargs:
    #         - include_platform: flag to include platform contract addresses (eg. 0x.... for Ethereum based tokens). valid values: true, false
    #     :return:
    #                id       symbol     name
    #        1    bitcoin    btc        Bitcoin
    #     """
    #
    #     api_url = '{0}coins/list'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return pd.DataFrame(self.__request(api_url))
    #
    # @func_args_preprocessing
    # def get_coins_markets(self, vs_currency, **kwargs):
    #     """
    #     获取币当前基本信息，最新价格，市值，市值排名，交易量，流通量，最大供应量等市场相关信息
    #     List all supported coins price, market cap, volume, and market related data
    #     :param vs_currency: The target currency of market data (usd, eur, jpy, etc.)
    #     :param kwargs:
    #         - ids: The ids of the coin, comma separated crytocurrency symbols (base). refers to /coins/list.
    #                When left empty, returns numbers the coins observing the params limit and start
    #         - category: filter by coin category. Refer to /coin/categories/list
    #         - order: valid values: market_cap_desc, gecko_desc, gecko_asc, market_cap_asc, market_cap_desc, volume_asc, volume_desc, id_asc, id_desc
    #                 sort results by field. Default value : market_cap_desc
    #         - per_page: valid values: 1..250; Total results per page; Default value : 100
    #         - int page: Page through results; Default value : 1
    #         - boolean sparkline: Include sparkline 7 days data (eg. true, false), Default value : false
    #         - str price_change_percentage: Include price change percentage in 1h, 24h, 7d, 14d, 30d, 200d, 1y (eg. '1h,24h,7d' comma-separated, invalid values will be discarded)
    #
    #         - bool all_data: 是否一次性获取全部币的信息，默认为False
    #     :return:
    #     """
    #
    #     kwargs['vs_currency'] = vs_currency
    #     api_url = '{0}coins/markets'.format(self.api_base_url)
    #     all_data = kwargs.pop('all_data', False)
    #     if all_data == 'true':
    #         res_list = []
    #         page_num = 1
    #         Flag = True
    #         while Flag and (page_num < 10000):
    #             kwargs['page'] = page_num
    #             kwargs['per_page'] = 250
    #             api_url_kwargs = self.__api_url_params(api_url, kwargs)
    #             res = pd.DataFrame(self.__request(api_url_kwargs))
    #             if res.empty:
    #                 Flag = False  # 数据获取完毕
    #             else:
    #                 res_list.append(res)
    #                 page_num = page_num + 1
    #             time.sleep(5)
    #         all_df = pd.concat(res_list, ignore_index=True)
    #         return all_df
    #     else:
    #         api_url_kwargs = self.__api_url_params(api_url, kwargs)
    #         res = pd.DataFrame(self.__request(api_url_kwargs))
    #         return res
    #
    # @func_args_preprocessing
    # def get_coin_by_id(self, id, **kwargs):
    #     """
    #     获取币的最新基本信息如：出块时间， 加密算法，类别，世纪块时间，舆情评分信息，各币种的最新价格数据及相关统计数据，
    #                       社区followers信息评论信息等，开发者信息，以及各交易所最新的交易信息（只显示100条记录，更详细的需要通过get_coin_ticker_by_id获取）
    #     Get current data (name, price, market, ... including exchange tickers) for a coin
    #     IMPORTANT:
    #     Ticker object is limited to 100 items, to get more tickers, use /coins/{id}/tickers
    #     Ticker is_stale is true when ticker that has not been updated/unchanged from the exchange for a while.
    #     Ticker is_anomaly is true if ticker's price is outliered by our system.
    #
    #     :param id: pass the coin id (can be obtained from /coins) eg. bitcoin
    #     :param kwargs:
    #         - localization： Include all localized languages in response (true/false) [default: true] # 各语言下的名字翻译
    #         - tickers： Include tickers data (true/false) [default: true] # 是否包含各交易所最新的交易信息， 最多只包含100条，更多的数据需要get_coin_ticker_by_id获取
    #         - market_data：Include market_data (true/false) [default: true]
    #         - community_data：Include community_data data (true/false) [default: true]
    #         - developer_data：Include developer_data data (true/false) [default: true]
    #         - sparkline：Include sparkline 7 days data (eg. true, false) [default: false]
    #     :return:
    #     """
    #
    #     api_url = '{0}coins/{1}/'.format(self.api_base_url, id)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # @func_args_preprocessing
    # def get_coin_ticker_by_id(self, id, **kwargs):
    #     """
    #     获取最新各家交易所关于该币种的交易数据（交易对名称id信息，交易所名称，交易对价格，交易额，bid ask价差，数据是否异常等信息），每次获取100个； 可以自己拼接。
    #     该函数是get_coin_by_id中ticker_data的完整版
    #
    #     Get coin tickers (paginated to 100 items)
    #     IMPORTANT:
    #     Ticker is_stale is true when ticker that has not been updated/unchanged from the exchange for a while.
    #     Ticker is_anomaly is true if ticker's price is outliered by our system.
    #     You are responsible for managing how you want to display these information (e.g. footnote, different background, change opacity, hide)
    #
    #     :param id: pass the coin id (can be obtained from /coins/list) eg. bitcoin
    #     :param kwargs:
    #         - int exchange_ids：filter results by exchange_ids (ref: v3/exchanges/list)
    #         - include_exchange_logo： flag to show exchange_logo
    #         - int page：Page through results
    #         - order:valid values: trust_score_desc (default), trust_score_asc and volume_desc
    #         - depth:flag to show 2% orderbook depth. valid values: true, false
    #     :return:
    #     """
    #
    #     api_url = '{0}coins/{1}/tickers'.format(self.api_base_url, id)
    #
    #     all_data = kwargs.pop('all_data', False)
    #     if all_data == 'true':
    #         tickers_list = []
    #         page_num = 1
    #         Flag = True
    #         while Flag and (page_num < 100):
    #             kwargs['page'] = page_num
    #             kwargs['order'] = 'volume_desc'
    #             api_url_kwargs = self.__api_url_params(api_url, kwargs)
    #             res = self.__request(api_url_kwargs)
    #             if (page_num == 1):
    #                 tickers_list = res['tickers']
    #                 page_num = page_num + 1
    #             elif (page_num != 1) and res:
    #                 tickers_list.extend(res['tickers'])
    #                 page_num = page_num + 1
    #             elif not res:
    #                 Flag = False
    #             time.sleep(5)
    #
    #         all_tickers = pd.DataFrame(tickers_list)
    #         return all_tickers
    #     else:
    #         api_url = self.__api_url_params(api_url, kwargs)
    #         return self.__request(api_url)
    #
    # @func_args_preprocessing
    # def get_coin_history_by_id(self, id, date, **kwargs):
    #     """
    #     获取币的某天的基本信息如：对应不同法币或常用币种的的价格，市值，交易额数据； 社区followers信息评论信息等； 开发者信息
    #     Get historical data (name, price, market, stats) at a given date for a coin
    #     :param id:
    #     :param date:
    #     :param kwargs:
    #     :return:
    #     """
    #
    #     kwargs['date'] = date
    #
    #     api_url = '{0}coins/{1}/history'.format(self.api_base_url, id)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #     res = self.__request(api_url)
    #     # res = pd.DataFrame(self.__request(api_url))
    #     # pd.DataFrame(res)
    #     return res
    #
    # @func_args_preprocessing
    # def get_coin_market_chart_by_id(self, symbol, days, **kwargs):
    #     """
    #     获取该币的今日至days天前的历史收盘价，市值，24小时交易量（BTC计价），交易额（usd）
    #
    #     Get historical market data include price, market cap, and 24h volume (granularity auto)
    #     Minutely data will be used for duration within 1 day,
    #     Hourly data will be used for duration between 1 day and 90 days,
    #     Daily data will be used for duration above 90 days.
    #
    #     :param symbol: 币简称，原始接口需要输入的是id， 但是id不好记，所以输入symbol，下面会自动转换为id
    #     :param days: Data up to number of days ago (eg. 1,14,30,max)
    #     :param kwargs:
    #         - vs_currency: The target currency of market data (usd, eur, jpy, etc.)
    #         - interval: Data interval. Possible value: daily
    #     :return:
    #     """
    #     id = common_crypto[symbol]
    #     vs_currency = 'usd'
    #     api_url = '{0}coins/{1}/market_chart?vs_currency={2}&days={3}'.format(self.api_base_url, id, vs_currency, days)
    #     api_url = self.__api_url_params(api_url, kwargs, api_url_has_params=True)
    #     res_usd = self.__request(api_url)
    #
    #     prices_df = pd.DataFrame(res_usd['prices'], columns=['date', 'close']).set_index('date')
    #     market_caps_df = pd.DataFrame(res_usd['market_caps'], columns=['date', 'market_cap']).set_index('date')
    #     amount_df = pd.DataFrame(res_usd['total_volumes'], columns=['date', 'amount']).set_index('date')
    #
    #     if symbol in fiat_crypto.keys():
    #         vs_currency = symbol
    #         api_url = '{0}coins/{1}/market_chart?vs_currency={2}&days={3}'.\
    #             format(self.api_base_url, id, vs_currency, days)
    #         api_url = self.__api_url_params(api_url, kwargs, api_url_has_params=True)
    #         res_btc = self.__request(api_url)
    #         volumes_df = pd.DataFrame(res_btc['total_volumes'], columns=['date', 'volume']).set_index('date')
    #         all_df = pd.concat([prices_df, market_caps_df, amount_df, volumes_df], axis=1)
    #     else:
    #         all_df = pd.concat([prices_df, market_caps_df, amount_df], axis=1)
    #
    #     all_df.reset_index(inplace=True)
    #     all_df['date'] = pd.to_datetime(all_df['date'], unit='ms').dt.tz_localize('UTC').dt.tz_convert('Asia/Shanghai')
    #     # all_df['date'] = pd.to_datetime(all_df['date'], unit='ms').dt.tz_localize('UTC').dt.tz_convert('Europe/London')
    #     all_df['date'] = all_df['date'].astype(str)
    #     all_df = all_df[all_df['date'].str.contains('08:00:00')]  # 最新的一天数据还不完整，剔除掉
    #     all_df['date'] = all_df['date'].str[:10]
    #     all_df = all_df.set_index('date')
    #     all_df = all_df.shift(-1)
    #     all_df = all_df.iloc[:-1, :]
    #     return all_df
    #
    # @func_args_preprocessing
    # def get_coin_market_chart_range_by_id(self, symbol, start_date, end_date, **kwargs):
    #     """
    #     获取该币的start_date到end_date之间的收盘价，市值，24小时交易量（BTC计价），交易额（usd）， 这里比上面的函数更好用些
    #     # 注意 2015-01-28的数据有缺失
    #     Get historical market data include price, market cap, and 24h volume within a range of timestamp (granularity auto)
    #     Data granularity is automatic (cannot be adjusted)
    #     1 day from query time = 5 minute interval data
    #     1 - 90 days from query time = hourly data
    #     above 90 days from query time = daily data (00:00 UTC)
    #
    #     :param symbol: 币简称，原始接口需要输入的是id， 但是id不好记，所以输入symbol，下面会自动转换为id
    #     :param start_date: 格式为‘2015-01-01’， 代码里面需要转换为From date in UNIX Timestamp (eg. 1392577232)
    #     :param end_date: 格式为‘2015-01-01’， 代码里面需要转换为To date in UNIX Timestamp (eg. 1422577232)
    #     :param kwargs:
    #     :return:
    #     """
    #
    #     id = common_crypto[symbol]
    #     vs_currency = 'usd'
    #     from_timestamp = util_str_to_Unix_timestamp(start_date, tz_str=' +0000')
    #     to_timestamp = util_str_to_Unix_timestamp(end_date, tz_str=' +0000')
    #
    #     api_url = '{0}coins/{1}/market_chart/range?vs_currency={2}&from={3}&to={4}'.\
    #         format(self.api_base_url, id, vs_currency, from_timestamp, to_timestamp)
    #     api_url = self.__api_url_params(api_url, kwargs, api_url_has_params=True)
    #     res_usd = self.__request(api_url)
    #
    #     prices_df = pd.DataFrame(res_usd['prices'], columns=['date', 'close']).set_index('date')
    #     market_caps_df = pd.DataFrame(res_usd['market_caps'], columns=['date', 'market_cap']).set_index('date')
    #     amount_df = pd.DataFrame(res_usd['total_volumes'], columns=['date', 'amount']).set_index('date')
    #
    #     if symbol in fiat_crypto.keys():
    #         vs_currency = symbol
    #         api_url = '{0}coins/{1}/market_chart/range?vs_currency={2}&from={3}&to={4}'. \
    #             format(self.api_base_url, id, vs_currency, from_timestamp, to_timestamp)
    #         api_url = self.__api_url_params(api_url, kwargs, api_url_has_params=True)
    #         res_btc = self.__request(api_url)
    #         volumes_df = pd.DataFrame(res_btc['total_volumes'], columns=['date', 'volume']).set_index('date')
    #         all_df = pd.concat([prices_df, market_caps_df, amount_df, volumes_df], axis=1)
    #     else:
    #         all_df = pd.concat([prices_df, market_caps_df, amount_df], axis=1)
    #
    #     all_df.reset_index(inplace=True)
    #     all_df['date'] = pd.to_datetime(all_df['date'], unit='ms').dt.tz_localize('UTC').dt.tz_convert('+0000')
    #     all_df['date'] = all_df['date'].astype(str)
    #     # all_df = all_df.iloc[:-1, :]  # 最新的一天数据还不完整，剔除掉
    #     all_df['date'] = all_df['date'].str[:10]
    #     all_df = all_df.set_index('date')
    #     all_df = all_df.shift(-1)
    #     all_df = all_df.iloc[:-1, :]
    #
    #     # 注意 2015-01-28的数据有缺失, 直接填充为27号的数据
    #     all_df.loc['2015-01-28', :] = all_df.loc['2015-01-27', :]
    #     all_df.sort_index(ascending=True, inplace=True)
    #
    #     return all_df
    #
    # @func_args_preprocessing
    # def get_coin_status_updates_by_id(self, symbol, **kwargs):
    #     """
    #     关于该币的最新的协议修改，重大新闻等信息
    #     Get status updates for a given coin
    #     :param symbol:
    #     :param kwargs:
    #     :return:
    #     """
    #
    #     id = common_crypto[symbol]
    #     api_url = '{0}coins/{1}/status_updates'.format(self.api_base_url, id)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #     res = self.__request(api_url)
    #     return res
    #
    # @func_args_preprocessing
    # def get_coin_ohlc_by_id(self, symbol, days, **kwargs):
    #     """
    #     # 数据质量可能有点问题， 部分数据的前一条收盘跟下一条的开盘数据相差较大
    #     # 目前数据不支持日K线，需要日K线的话，需要根据四小时线进行组合
    #
    #     Get coin's OHLC
    #     Candle's body:
    #     1 - 2 days: 30 minutes
    #     3 - 30 days: 4 hours
    #     31 and before: 4 days
    #
    #     :param symbol:
    #     :param vs_currency:
    #     :param days:
    #     :param kwargs:
    #     :return:
    #     """
    #
    #     id = common_crypto[symbol]
    #     vs_currency = 'usd'
    #     api_url = '{0}coins/{1}/ohlc?vs_currency={2}&days={3}'.format(self.api_base_url, id, vs_currency, days)
    #     api_url = self.__api_url_params(api_url, kwargs, api_url_has_params=True)
    #
    #     res = self.__request(api_url)
    #     ohlc_df = pd.DataFrame(res, columns=['date', 'open', 'high', 'low', 'close'])
    #     ohlc_df['date'] = pd.to_datetime(ohlc_df['date'], unit='ms').dt.tz_localize('UTC').dt.tz_convert(tz='+0000')
    #     ohlc_df['date'] = ohlc_df['date'].astype(str)
    #     ohlc_df['date'] = ohlc_df['date'].str[:19]
    #     ohlc_df = ohlc_df.set_index('date')
    #     ohlc_df = ohlc_df.shift(-1)
    #     ohlc_df = ohlc_df.iloc[:-1, :]
    #     return ohlc_df
    #
    # # ---------- Contract ----------#
    # @func_args_preprocessing
    # def get_coin_info_from_contract_address_by_id(self, id, contract_address, **kwargs):
    #     """Get coin info from contract address"""
    #
    #     api_url = '{0}coins/{1}/contract/{2}'.format(self.api_base_url, id, contract_address)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # @func_args_preprocessing
    # def get_coin_market_chart_from_contract_address_by_id(self, id, contract_address, vs_currency, days, **kwargs):
    #     """Get historical market data include price, market cap, and 24h volume (granularity auto) from a contract address"""
    #
    #     api_url = '{0}coins/{1}/contract/{2}/market_chart/?vs_currency={3}&days={4}'.format(self.api_base_url, id,
    #                                                                                         contract_address,
    #                                                                                         vs_currency, days)
    #     api_url = self.__api_url_params(api_url, kwargs, api_url_has_params=True)
    #
    #     return self.__request(api_url)
    #
    # @func_args_preprocessing
    # def get_coin_market_chart_range_from_contract_address_by_id(self, id, contract_address, vs_currency, from_timestamp,
    #                                                             to_timestamp, **kwargs):
    #     """Get historical market data include price, market cap, and 24h volume within a range of timestamp (granularity auto) from a contract address"""
    #
    #     api_url = '{0}coins/{1}/contract/{2}/market_chart/range?vs_currency={3}&from={4}&to={5}'.format(
    #         self.api_base_url, id, contract_address, vs_currency, from_timestamp, to_timestamp)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # # ---------- ASSET PLATFORMS ----------#
    # @func_args_preprocessing
    # def get_asset_platforms(self, **kwargs):
    #     """List all asset platforms (Blockchain networks)"""
    #
    #     api_url = '{0}asset_platforms'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # # ---------- CATEGORIES ----------#
    # @func_args_preprocessing
    # def get_coins_categories_list(self, **kwargs):
    #     """List all categories"""
    #
    #     api_url = '{0}coins/categories/list'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # @func_args_preprocessing
    # def get_coins_categories(self, **kwargs):
    #     """List all categories with market data"""
    #
    #     api_url = '{0}coins/categories'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # # ---------- EXCHANGES ----------#
    # @func_args_preprocessing
    # def get_exchanges_list(self, **kwargs):
    #     """List all exchanges"""
    #
    #     api_url = '{0}exchanges'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # @func_args_preprocessing
    # def get_exchanges_id_name_list(self, **kwargs):
    #     """List all supported markets id and name (no pagination required)"""
    #
    #     api_url = '{0}exchanges/list'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # @func_args_preprocessing
    # def get_exchanges_by_id(self, id, **kwargs):
    #     """Get exchange volume in BTC and tickers"""
    #
    #     api_url = '{0}exchanges/{1}'.format(self.api_base_url, id)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # @func_args_preprocessing
    # def get_exchanges_tickers_by_id(self, id, **kwargs):
    #     """Get exchange tickers (paginated, 100 tickers per page)"""
    #
    #     api_url = '{0}exchanges/{1}/tickers'.format(self.api_base_url, id)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # @func_args_preprocessing
    # def get_exchanges_status_updates_by_id(self, id, **kwargs):
    #     """Get status updates for a given exchange"""
    #
    #     api_url = '{0}exchanges/{1}/status_updates'.format(self.api_base_url, id)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # @func_args_preprocessing
    # def get_exchanges_volume_chart_by_id(self, id, days, **kwargs):
    #     """
    #     获取交易所交易量，单位为BTC； 不确定是现货交易量还是合约交易量还是总量
    #
    #     Get volume chart data for a given exchange
    #     :param id:
    #     :param days:
    #     :param kwargs:
    #     :return:
    #     """
    #
    #
    #     kwargs['days'] = days
    #
    #     api_url = '{0}exchanges/{1}/volume_chart'.format(self.api_base_url, id)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # # ---------- FINANCE ----------#
    # @func_args_preprocessing
    # def get_finance_platforms(self, **kwargs):
    #     """Get cryptocurrency finance platforms data"""
    #
    #     api_url = '{0}finance_platforms'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # @func_args_preprocessing
    # def get_finance_products(self, **kwargs):
    #     """Get cryptocurrency finance products data"""
    #
    #     api_url = '{0}finance_products'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # # ---------- INDEXES ----------#
    # @func_args_preprocessing
    # def get_indexes(self, **kwargs):
    #     """List all market indexes"""
    #
    #     api_url = '{0}indexes'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # # @func_args_preprocessing
    # # def get_indexes_by_id(self, id, **kwargs):
    # #    """Get market index by id"""
    # #
    # #    api_url = '{0}indexes/{1}'.format(self.api_base_url, id)
    # #    api_url = self.__api_url_params(api_url, kwargs)
    # #
    # #    return self.__request(api_url)
    #
    # @func_args_preprocessing
    # def get_indexes_by_market_id_and_index_id(self, market_id, id, **kwargs):
    #     """Get market index by market id and index id"""
    #
    #     api_url = '{0}indexes/{1}/{2}'.format(self.api_base_url, market_id, id)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # @func_args_preprocessing
    # def get_indexes_list(self, **kwargs):
    #     """List market indexes id and name"""
    #
    #     api_url = '{0}indexes/list'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # # ---------- DERIVATIVES ----------#
    # @func_args_preprocessing
    # def get_derivatives(self, **kwargs):
    #     """
    #     获取当前所有衍生品的最新相关信息，包含价格，24h涨跌幅，合约类型，基差，报价基点，资金费率，开仓量，24h交易量, 最近交易时间，合约交割时间
    #
    #     List all derivative tickers
    #     :param kwargs:
    #     :return:
    #     """
    #
    #     api_url = '{0}derivatives'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #     res = self.__request(api_url)
    #     all_df = pd.DataFrame(res)
    #     return all_df
    #
    # @func_args_preprocessing
    # def get_derivatives_exchanges(self, **kwargs):
    #     """"
    #     获取当前交易所衍生品最新汇总统计信息，包含合约总开仓量，24h成交量，永续合约对数，总合约对个数
    #     """
    #     """List all derivative tickers"""
    #
    #     api_url = '{0}derivatives/exchanges'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #     res = self.__request(api_url)
    #     all_df = pd.DataFrame(res)
    #     return all_df
    #
    # @func_args_preprocessing
    # def get_derivatives_exchanges_by_id(self, id, **kwargs):
    #     """List all derivative tickers"""
    #
    #     api_url = '{0}derivatives/exchanges/{1}'.format(self.api_base_url, id)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # @func_args_preprocessing
    # def get_derivatives_exchanges_list(self, **kwargs):
    #     """List all derivative tickers"""
    #
    #     api_url = '{0}derivatives/exchanges/list'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # # ---------- STATUS UPDATES ----------#
    # @func_args_preprocessing
    # def get_status_updates(self, **kwargs):
    #     """List all status_updates with data (description, category, created_at, user, user_title and pin)"""
    #
    #     api_url = '{0}status_updates'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # # ---------- EVENTS ----------#
    # @func_args_preprocessing
    # def get_events(self, **kwargs):
    #     """Get events, paginated by 100"""
    #
    #     api_url = '{0}events'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # @func_args_preprocessing
    # def get_events_countries(self, **kwargs):
    #     """Get list of event countries"""
    #
    #     api_url = '{0}events/countries'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # @func_args_preprocessing
    # def get_events_types(self, **kwargs):
    #     """Get list of event types"""
    #
    #     api_url = '{0}events/types'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # # ---------- EXCHANGE-RATES ----------#
    # @func_args_preprocessing
    # def get_exchange_rates(self, **kwargs):
    #     """Get BTC-to-Currency exchange rates"""
    #
    #     api_url = '{0}exchange_rates'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # # ---------- TRENDING ----------#
    # @func_args_preprocessing
    # def get_search_trending(self, **kwargs):
    #     """Get top 7 trending coin searches"""
    #
    #     api_url = '{0}search/trending'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)
    #
    # # ---------- GLOBAL ----------#
    # @func_args_preprocessing
    # def get_global(self, **kwargs):
    #     """
    #     各币的最新市值，成交量，市值占比
    #     :param kwargs:
    #     :return:
    #     """
    #     """Get cryptocurrency global data"""
    #
    #     api_url = '{0}global'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)['data']
    #
    # @func_args_preprocessing
    # def get_global_decentralized_finance_defi(self, **kwargs):
    #     """Get cryptocurrency global decentralized finance(defi) data"""
    #
    #     api_url = '{0}global/decentralized_finance_defi'.format(self.api_base_url)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)['data']
    #
    # # ---------- COMPANIES ----------#
    # @func_args_preprocessing
    # def get_companies_public_treasury_by_coin_id(self, coin_id, **kwargs):
    #     """
    #     各机构公司持有的币的量
    #     Get public companies data
    #
    #     :param coin_id: bitcoin or ethereum
    #     :return:
    #
    #     """
    #
    #     api_url = '{0}companies/public_treasury/{1}'.format(self.api_base_url, coin_id)
    #     api_url = self.__api_url_params(api_url, kwargs)
    #
    #     return self.__request(api_url)


if __name__ == '__main__':
    # symbols = QA_fetch_bitmex_symbols()
    # start = time.mktime(datetime.datetime(2020, 6, 13, tzinfo=tzutc()).timetuple())
    # end = time.mktime(datetime.datetime(2020, 7, 14, tzinfo=tzutc()).timetuple())
    # a = QA_fetch_bitmex_kline_with_auto_retry('DOGEUSDT', start, end, '1d')

    # symbol = 'BTC-USDT'
    symbol = 'BTC-USD'
    timezone = 'Asia/Shanghai'
    # timezone = '+0000'

    if symbol == 'BTC-USDT':
        asset = 'BTC-USDT'
        market_type = 'um_futures'
    elif symbol == 'BTC-USD':
        asset = 'BTC-USD'
        market_type = 'cm_futures'

    bm = BitMEXAPI()

    # 测试 get_funding_rate
    df = bm.get_funding_rate(symbol=symbol, all_data=True)

    df['timestamp'] = df.apply(lambda x: int(x['timestamp'] / 1000), axis=1)
    df['date'] = pd.to_datetime(df['timestamp'], unit='s').dt.tz_localize('UTC').dt.tz_convert(timezone)
    df['date'] = df['date'].dt.strftime('%Y-%m-%d')
    df['datetime'] = pd.to_datetime(df['timestamp'], unit='s').dt.tz_localize('UTC').dt.tz_convert(timezone)
    df['datetime'] = df['datetime'].dt.strftime('%Y-%m-%d %H:%M:%S')
    df['time'] = pd.to_datetime(df['timestamp'], unit='s').dt.tz_localize('UTC').dt.tz_convert(timezone)
    df['time'] = df['time'].dt.strftime('%H:%M:%S')
    df.set_index('datetime', inplace=True)

    import os
    from quant_researcher.quant.project_tool.localize import DATA_DIR
    file_path = os.path.join(DATA_DIR, f'funding_rate')
    os.makedirs(file_path, exist_ok=True)
    file_name = os.path.join(file_path, f'bitmex_{symbol}_funding_rate')
    df.to_excel(f'{file_name}.xlsx')